// Copyright 2008 web2me.dev
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package net.web2me.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import net.web2me.util.HttpBasicAuthenticator;
import net.web2me.util.SiteStreamGenerator;
import net.web2me.util.UtilityException;

import org.apache.log4j.Logger;

/**
 * Generates stream from a site
 * 
 * @author web2me
 */
public class SiteStreamGenerator {

	private static Logger logger = Logger.getLogger(SiteStreamGenerator.class);
	private static int retryOnTimeOut = 20;

	/**
	 * Generates stream from the given URL, setting previously the cookies.<br>
	 * The cookies parameters should look like the following:<br>
	 * 
	 * <pre>
	 * key1=value1;key2=value2
	 * </pre>
	 * 
	 * @param url
	 *            The URL to get the stream
	 * @param cookies
	 *            A semicolon separated key-value cookies
	 * @return A string with the URL content
	 * @throws UtilityException
	 */
	public static String getStream(String url, String cookies)
			throws UtilityException {
		URL urlSite = null;
		try {
			urlSite = new URL(url);
		} catch (MalformedURLException e) {
			throw new UtilityException(e);
		}

		try {
			URLConnection conn = urlSite.openConnection();

			if (cookies != null) {
				conn.setRequestProperty("Cookie", cookies);
			}

			conn.setConnectTimeout(0);
		} catch (IOException e2) {
			logger.error("IOException caught", e2);
			throw new UtilityException(e2);
		}

		String finalStream = null;

		logger.info("Loading site stream for \"" + url + "\"");
		try {
			finalStream = getStringFromStream(urlSite.openStream());
		} catch (Exception e) {
			if (e.getMessage().indexOf(
					"Server returned HTTP response code: 401") > -1) {
				try {
					finalStream = getStreamWithAuthentication(url);
				} catch (MalformedURLException e1) {
					logger.error("MalformedURLException caught", e1);
					throw new UtilityException(e1);
				} catch (IOException e3) {
					logger.error("IOException caught", e);
					throw new UtilityException(e3);
				}
			} else {
				if (retryOnTimeOut > 0) {
					logger.warn("Timeout detected. Retrying...");
					retryOnTimeOut--;

					finalStream = getStream(url);
				} else {
					throw new UtilityException(e);
				}
			}
		}

		if (finalStream == null) {
			throw new UtilityException("The stream couldn't be loaded");
		}

		return finalStream;
	}

	/**
	 * Generates stream from the given URL
	 * 
	 * @param url
	 *            The URL to get the stream
	 * @return A string with the URL content
	 * @throws UtilityException
	 */
	public static String getStream(String url) throws UtilityException {
		return getStream(url, null);
	}

	private static String getStreamWithAuthentication(String url)
			throws MalformedURLException, IOException {
		logger.info("Loading stream from an authenticated URL");

		int dobleDot = url.lastIndexOf(":");
		int begin = "http://".length();
		int at = url.indexOf("@");

		String username = url.substring(begin, dobleDot);
		String password = url.substring(dobleDot + 1, at);

		Authenticator auth = new HttpBasicAuthenticator(username, password);
		Authenticator.setDefault(auth);

		URL authUrl = new URL(url);

		return getStringFromStream(authUrl.openStream());

	}

	private static String getStringFromStream(InputStream is)
			throws IOException {
		StringBuilder siteString = new StringBuilder();
		BufferedReader br = null;

		br = new BufferedReader(new InputStreamReader(is));

		String line = "";

		while ((line = br.readLine()) != null) {
			siteString.append(line);
		}

		return siteString.toString();
	}

	/**
	 * Return the host from an URL
	 * 
	 * @param url
	 *            The URL
	 * @return The host
	 * @throws UtilityException
	 */
	public static String getHost(String url) throws UtilityException {
		URL urlSite = null;
		try {
			urlSite = new URL(url);
		} catch (MalformedURLException e) {
			throw new UtilityException(e);
		}

		return urlSite.getHost();
	}

	/**
	 * Returns the query part from the URL
	 * 
	 * @param url
	 *            The URL
	 * @return The query part
	 * @throws UtilityException
	 */
	public static String getQuery(String url) throws UtilityException {
		URL urlSite = null;
		try {
			urlSite = new URL(url);
		} catch (MalformedURLException e) {
			throw new UtilityException(e);
		}

		return urlSite.getQuery();
	}
}
